
#include "idt.h"
#include "config.h"
#include "memory/memory.h"
#include "textmode/textmode.h"
#include "io/io.h"
#include "task/task.h"
#include "kernel.h"
#include "isr80h/isr80h.h"

struct idt_desc idt_descriptors[TESTOS_TOTAL_INTERRUPTS]; 
struct idtr_desc idtr_descriptor; 
static ISR80H_COMMAND isr80h_commands[TESTOS_MAX_ISR80H_COMMANDS]; 

extern void idt_load(struct idtr_desc* ptr); 
extern void int21h(); 
extern void no_interrupt(); 
extern void isr80h_wrapper();

void idt_zero()
{
    print("Divide by zero exception\n"); 
}

void int21h_handler() 
{
    print("Keyboard pressed"); 
    outb(0x20, 0x20); 
}

void no_interrupt_handler()
{
    outb(0x20, 0x20); 
}

/* 
 * Sets an interrupt entry
 *
 * interrupt_no - number of an interrupt 
 * address      - the address of interrupt callback 
 */
void idt_set(size_t interrupt_no, void* address)
{
    struct idt_desc* desc = &idt_descriptors[interrupt_no]; 
    desc->offset_1 = (uint32_t)address & 0x0000ffff; 
    desc->selector = TESTOS_KERNEL_CODE_SELECTOR;
    desc->zero = 0x00;
    desc->type_attr = 0xEE; 
    desc->offset_2 = (uint32_t)address >> 16;  
}

void idt_init()
{
    int i; 
    memset(idt_descriptors, 0, sizeof(idt_descriptors)); 
    memset(isr80h_commands, 0, sizeof(isr80h_commands)); 
    isr80h_register_commands(); 
    idtr_descriptor.limit = sizeof(idt_descriptors) - 1;
    idtr_descriptor.base = (uint32_t)idt_descriptors; // pointer to idt_descriptors 
    
    for (i = 0; i < TESTOS_TOTAL_INTERRUPTS; i++) {
        idt_set(i, no_interrupt); 
    }

    idt_set(0, idt_zero); // override divide by zero exception
    idt_set(0x21, int21h); 
    idt_set(0x80, isr80h_wrapper); 

    // load of IDT
    idt_load(&idtr_descriptor); 

    enable_interrupts(); 
}

void* isr80h_handle_command(const int command, struct interrupt_frame* frame)
{
    void* result = 0; 
    if (command < 0 || command >= TESTOS_MAX_ISR80H_COMMANDS) {
        // invaid command
        return 0; 
    }

    ISR80H_COMMAND command_function = isr80h_commands[command]; 
    if (!command_function) {
        // command does not exist
        return 0; 
    }

    result = command_function(frame); 
    return result; 
}

void* isr80h_handler(const int command, struct interrupt_frame* frame)
{   
    void* res = 0; 
    kernel_page(); 

    task_current_save_state(frame); 
    res = isr80h_handle_command(command, frame); 
    task_page(); 
    return res; 
}

void isr80h_register_command(const int command, ISR80H_COMMAND command_func)
{
    if (command < 0 || command >= TESTOS_MAX_ISR80H_COMMANDS) {
        panic("isr80h_residter_command(): command id out of bounds"); 
    } 

    if (isr80h_commands[command]) {
        panic("isr80h_residter_command(): command already exist");
    }

    isr80h_commands[command] = command_func; 


}